#include <rtthread.h>
#include <rtdevice.h>

#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>

#include "procfs.h"

struct procfs_entry
{
    const char *path;
    const struct dfs_file_ops *ops;
};

static const struct procfs_entry _entris[] =
{
    {"meminfo", &_meminfo_fops},
    {"thread", &_thread_fops}
};

static int dfs_procfs_open(struct dfs_fd *file)
{
    int ret = 0;
    struct dfs_filesystem *fs;
    int i;

    fs = (struct dfs_filesystem *)file->data;
    if (file->flags & O_DIRECTORY)
    {
        if (rt_strcmp(file->path, "/"))
            return -ENOENT;

        file->pos = 0;
        file->size = sizeof(_entris)/sizeof(_entris[0]);

        return 0;
    }

    for (i = 0; i < sizeof(_entris)/sizeof(_entris[0]); i ++)
    {
        const struct procfs_entry *e = &_entris[i];

        if (rt_strcmp(&file->path[1], e->path) == 0)
        {
            ret = e->ops->open(file);
            if (ret == 0)
                file->fops = e->ops;

            break;
        }
    }

    return ret;
}

static int dfs_procfs_getdents(struct dfs_fd *file, struct dirent *dirp, uint32_t count)
{
    uint32_t index;
    struct dirent *d;

    /* make integer count */
    count = (count / sizeof(struct dirent));
    if (count == 0)
        return -EINVAL;

    for (index = 0; (index < count) && ((index + file->pos) < file->size); index ++)
    {
        const struct procfs_entry *e = &_entris[file->pos + index];

        d = dirp + index;
        d->d_type = DT_REG;
        d->d_namlen = rt_strlen(e->path);
        d->d_reclen = (uint16_t)sizeof(struct dirent);
        rt_strncpy(d->d_name, e->path, RT_NAME_MAX);
    }

    file->pos += index;

    return index * sizeof(struct dirent);
}

static int dfs_procfs_mount(struct dfs_filesystem* fs, unsigned long flag, const void* data)
{
    return 0;
}

static int dfs_procfs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st)
{
    /* stat root directory */
    if ((path[0] == '/') && (path[1] == '\0'))
    {
        st->st_dev = 0;

        st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
            S_IWUSR | S_IWGRP | S_IWOTH;
        st->st_mode &= ~S_IFREG;
        st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;

        st->st_size  = 0;
        st->st_mtime = 0;
    }
    else
    { //TODO
        st->st_dev = 0;

        st->st_mode = S_IRUSR | S_IRGRP | S_IROTH |
                S_IWUSR | S_IWGRP | S_IWOTH;
        st->st_mode |= S_IFREG;

        st->st_size  = 0;
        st->st_mtime = 0;
    }

    return 0;
}

static const struct dfs_file_ops _proc_fops = 
{
    dfs_procfs_open,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,                    /* flush */
    RT_NULL,                    /* lseek */
    dfs_procfs_getdents,
    RT_NULL,
};

static const struct dfs_filesystem_ops _proc_fs = 
{
    "procfs",
    DFS_FS_FLAG_DEFAULT,
    &_proc_fops,

    dfs_procfs_mount,
    RT_NULL,
    RT_NULL,
    RT_NULL,

    RT_NULL,
    dfs_procfs_stat,
    RT_NULL,
};

int procfs_init(void)
{
    int ret;
    const struct dfs_filesystem_ops *fs = &_proc_fs;

    ret = dfs_register(fs);
    if (ret == 0)
    {
        ret = dfs_pseudo_mount(":proc", fs, 0);
    }

    return ret;
}
INIT_COMPONENT_EXPORT(procfs_init);
